/*******************************************
 * Copyright 2007 Aphrodite Framework for javaME .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * Email : owner@aphroditeme.dev.java.net
 * Contributors:
 *     Kent Elton - initial API and implementation
 *******************************************/

package com.aphrodite.util;

import java.util.Vector;

/**
 * String manipulation utilities
 * 
 * @author kentelt
 * 
 */
public final class StringUtil {

	/**
	 * Space character.
	 */
	private final static char SPACE = 32;

	/**
	 * Encodes the string in the string buffer
	 * <p>
	 * Encodes a string so that it is suitable for transmission over HTTP
	 * </p>
	 * 
	 * @param string
	 * @return the encoded string
	 */
	public final static String encodeString(StringBuffer string) {
		StringBuffer encodedUrl = new StringBuffer(); // Encoded URL

		int len = string.length();
		// Encode each URL character
		final String UNRESERVED = "-_.!~*'()\"";
		for (int i = 0; i < len; i++) {
			char c = string.charAt(i); // Get next character
			if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
				// Alphanumeric characters require no encoding, append as is
				encodedUrl.append(c);
			} else {
				int imark = UNRESERVED.indexOf(c);
				if (imark >= 0) {
					// Unreserved punctuation marks and symbols require
					// no encoding, append as is
					encodedUrl.append(c);
				} else {
					// Encode all other characters to Hex, using the format
					// "%XX",
					// where XX are the hex digits
					encodedUrl.append('%'); // Add % character
					// Encode the character's high-order nibble to Hex
					encodedUrl.append(CharUtil.toHexChar((c & 0xF0) >> 4));
					// Encode the character's low-order nibble to Hex
					encodedUrl.append(CharUtil.toHexChar(c & 0x0F));
				}
			}
		}
		System.out.println("Encoded string " + encodedUrl.toString());
		return encodedUrl.toString(); // Return encoded URL
	}

	/**
	 * 
	 * @param base
	 *            String to compare
	 * @param end
	 *            Ending String
	 * @return <code>True</code> if <code>base</code> ends with <code>end</code>
	 */
	public static boolean endsWithIgnoreCase(String base, String end) {
		if (base.length() < end.length()) {
			return false;
		}
		return base.regionMatches(true, base.length() - end.length(), end, 0, end.length());
	}

	/**
	 * Checks if the two strings are equal irrespective of the case
	 * <p>
	 * For cdlc1.0 based devices
	 * </p>
	 * 
	 * @param str1
	 * @param str2
	 * @return <code>True</code> if the two strings are equal
	 */
	public static boolean equalsIgnoreCase(String str1, String str2) {

		if (str2 == null || str1.length() != str2.length()) {
			return false;
		}
		return str1.toLowerCase().equals(str2.toLowerCase());
	}

	/**
	 * Format a string like "Where is {0}, in the {1}" withe the corresponding
	 * <code>args</code> values.
	 * 
	 * @param pattern
	 * @param args
	 * @return The formated string
	 */
	public static String format(String pattern, Object[] args) {
		if (pattern != null) {
			StringBuffer toAppendTo = new StringBuffer();
			int l = pattern.length();
			int n = 0, lIndex = -1, lastIndex = 0;
			for (int i = 0; i < l; i++) {
				if (pattern.charAt(i) == '{') {
					n++;
					if (n == 1) {
						lIndex = i;
						toAppendTo.append(pattern.substring(lastIndex, i));
						lastIndex = i;
					}
				}
				if (pattern.charAt(i) == '}') {
					if (n == 1) {
						toAppendTo.append(processPattern(pattern.substring(lIndex + 1, i), args));
						lIndex = -1;
						lastIndex = i + 1;
					}
					n--;
				}
			}
			if (n > 0) {
				toAppendTo.append(processPattern(pattern.substring(lIndex + 1), args));
			} else {
				toAppendTo.append(pattern.substring(lastIndex));
			}
			return toAppendTo.toString();
		}
		return null;
	}

	/**
	 * Convert to UTF8 formatting to string
	 * 
	 * @param ao
	 * @return Converted string
	 */
	public static String fromUTF8(byte[] ao) {
		int nCharCode, i;
		int nLength = ao.length;
		char[] ach = new char[nLength];

		int nCount = 0;

		loop: for (i = 0; i < nLength; i++) {
			nCharCode = (ao[i]) & 0x00ff;
			if (nCharCode >= 0x80) {
				if (nCharCode < 0xe0) {
					// need 2 bytes
					nCharCode = (nCharCode & 0x1f) << 6;
					nCharCode |= ((ao[++i]) & 0x3f);
				} else {
					// need 3 bytes
					nCharCode = (nCharCode & 0x0f) << 12;
					nCharCode |= ((ao[++i]) & 0x3f) << 6;
					nCharCode |= ((ao[++i]) & 0x3f);
					// ignore character added by Notepad
					if (nCharCode == 0xfeff) {
						continue loop;
					}
				}
			}
			ach[nCount++] = (char) nCharCode;
		}
		return new String(ach, 0, nCount);
	}

	/**
	 * Checks if a String is empty ("") or null.
	 * 
	 * @param str
	 *            The String to check, may be null.
	 * @return true if the String is empty or null.
	 */
	public static boolean isEmpty(final String str) {
		boolean result = false;
		if (str == null || str.length() == 0) {
			result = true;
		}
		return result;
	}

	/**
	 * Join a string array into a string
	 * 
	 * @param string
	 * @return Concatenated string
	 */
	public static String join(String[] string) {
		StringBuffer buf = new StringBuffer();
		for (int i = 0; i < string.length; i++) {
			buf.append(string[i]);
		}
		return buf.toString();
	}

	/**
	 * pad a string on both sides to center it
	 * 
	 * @param str
	 *            string
	 * @param width
	 * @return Padded string
	 */
	public static String padCenter(final String str, final int width) {
		if (str.length() > width) {
			return str.substring(0, width);
		} else {
			boolean rigth = true;
			final StringBuffer padded = new StringBuffer(str);
			// noinspection MethodCallInLoopCondition
			while (padded.length() < width) {
				if (rigth) {
					padded.append(' ');
				} else {
					padded.insert(0, ' ');
				}
				rigth = !rigth;
			}
			return padded.toString();
		}
	}

	/**
	 * To return a right-aligned string with a certain maximum length. If the
	 * string's length is less than the target length it will be aligned to the
	 * right of the resulting string and the left will be filled with the given
	 * fill character.
	 * 
	 * @param sString
	 *            The string to be converted
	 * @param nWidth
	 *            The length of the result string
	 * @param cFill
	 *            The fill character
	 * 
	 * @return The resulting string
	 */
	public static String padLeft(String sString, int nWidth, char cFill) {
		if (nWidth > sString.length()) {
			StringBuffer sb = new StringBuffer(nWidth);

			for (int i = sString.length(); i < nWidth; i++) {
				sb.append(cFill);
			}

			sb.append(sString);
			sb.setLength(nWidth);

			return sb.toString();
		} else {
			return sString;
		}
	}

	/**
	 * To return a left-aligned string with a certain maximum length. If the
	 * string's length is less than the target length it will be aligned to the
	 * left of the resulting string and the right will be filled with the given
	 * fill character.
	 * 
	 * @param string
	 *            The string to be converted
	 * @param nWidth
	 *            The length of the result string
	 * @param cFill
	 *            The fill character
	 * 
	 * @return The resulting string
	 */
	public static String padRight(String string, int nWidth, char cFill) {
		if (nWidth > string.length()) {
			StringBuffer sb = new StringBuffer(nWidth);

			sb.append(string);

			for (int i = string.length(); i < nWidth; i++) {
				sb.append(cFill);
			}

			return sb.toString();
		} else {
			return string;
		}
	}

	/**
	 * Extracts N-th from an array of argumens.
	 * 
	 * @param indexString
	 *            a String number
	 * @param args
	 *            array of arguments
	 * @return The indexString-th parameter from the array
	 */
	private static String processPattern(String indexString, Object[] args) {
		try {
			int index = Integer.parseInt(indexString);
			if ((args != null) && (index >= 0) && (index < args.length)) {
				if (args[index] != null) {
					return args[index].toString();
				}
			}
		} catch (NumberFormatException nfe) {
			// NFE - nothing bad basically - the argument is not a number
			// swallow it for the time being and show default string
		}
		return "?";
	}

	/**
	 * Check if the given text contains alphabets A-Z or a-z or empty space
	 * 
	 * @param string
	 * @return true if the text contains alphabets
	 */
	public static boolean containsAlphabets(String string) {
		for (int i = 0; i < string.length(); i++) {
			if (CharUtil.isLetter(string.charAt(i))) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Check if the given text contains numbers
	 * 
	 * @param string
	 * @return true if the text contains numbers
	 */
	public static boolean containsNumbers(String string) {
		for (int i = 0; i < string.length(); i++) {
			if (CharUtil.isDigit(string.charAt(i)))
				return true;
		}
		return false;
	}

	/**
	 * Removes all spaces in a string
	 * 
	 * @param string
	 * @return String
	 */
	public static String removeAllSpaces(String string) {
		String s1 = "";
		for (int i = 0; i < string.length(); i++)
			if (string.charAt(i) != ' ')
				s1 = s1 + string.charAt(i);

		return s1.toLowerCase();
	}

	/**
	 * Remove extra spaces if they occur in a sequence
	 * 
	 * @param string
	 * @param removeLeadTrailSpaces
	 *            Flag to remove leading an trailing spaces
	 * @return String with spaces removed
	 */
	public static String removeExtraSpaces(String string, boolean removeLeadTrailSpaces) {
		StringBuffer buffer = new StringBuffer();
		if (removeLeadTrailSpaces)
			string = string.trim();
		byte wasASpace = 0;
		for (int i = 0; i < string.length(); i++) {
			if (string.charAt(i) == ' ' && wasASpace == 1) {
				continue;
			} else if (string.charAt(i) == ' ') {
				wasASpace = 1;
			} else
				wasASpace = 0;

			buffer.append(string.charAt(i));
		}

		return buffer.toString();
	}

	/**
	 * Replace all occurance of the given string
	 * 
	 * @param input
	 *            string
	 * @param search
	 * @param replace
	 *            string to replace
	 * @return Replaced string
	 */
	public static String replaceAll(String input, String search, String replace) {
		StringBuffer buffer = new StringBuffer();
		byte found = 0;

		for (int i = 0; i < input.length(); i++) {
			if (input.charAt(i) == search.charAt(0)) {
				found = 1;
				for (int j = 0; j < search.length(); j++) {
					if (input.charAt(i + j) != search.charAt(j)) {
						found = 0;
						break;
					}
				}

				if (found == 1) {
					buffer.append(replace);
					i += search.length() - 1;
					continue;
				}
			} else {
				buffer.append(input.charAt(i));
			}
		}

		return buffer.toString();
	}

	/**
	 * Replace the first occurance of the the <code>search</code> string
	 * 
	 * @param input
	 *            string
	 * @param search
	 *            string to find
	 * @param replace
	 *            string to replace
	 * @return Replaced string
	 */
	public static String replaceFirst(String input, String search, String replace) {
		int pos = input.indexOf(search);
		if (pos != -1) {
			input = input.substring(0, pos) + replace + input.substring(pos + search.length());
		}
		return input;
	}

	/**
	 * Replace the last occurance of the <code>search</code> string
	 * 
	 * @param input
	 *            string
	 * @param search
	 *            string to find
	 * @param replace
	 *            string to replace
	 * @return Replaced string
	 */
	public static String replaceLast(String input, String search, String replace) {
		int pos = input.indexOf(search);
		if (pos != -1) {
			int lastPos = pos;
			while (true) {
				pos = input.indexOf(search, lastPos + 1);
				if (pos == -1) {
					break;
				} else {
					lastPos = pos;
				}
			}
			input = input.substring(0, lastPos) + replace + input.substring(lastPos + search.length());
		}
		return input;
	}

	/**
	 * Reverse a given string
	 * 
	 * @param text
	 * @return reversed text
	 */
	public static String reverse(String text) {
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < text.length(); i++) {
			buffer.append(text.charAt(text.length() - 1 - i));
		}
		return buffer.toString();
	}

	/**
	 * Splits the provided text into an array, using whitespace as the
	 * separator.
	 * 
	 * @param str
	 *            the String to parse, may be null.
	 * @return an array of parsed Strings, null if null String input
	 */
	public static String[] split(final String str) {
		return split(str, null, -1);
	}

	/**
	 * Split a string into an array of string
	 * 
	 * @param toSplit
	 *            string to split
	 * @param delimiter
	 *            character
	 * @param ignoreEmpty
	 *            flag to ignore empty spaces
	 * @return array of string that is split at the given delimiter
	 */
	public static String[] split(String toSplit, char delimiter, boolean ignoreEmpty) {
		StringBuffer buffer = new StringBuffer();
		java.util.Stack stringStack = new java.util.Stack();
		try {
			for (int i = 0; i < toSplit.length(); i++) {
				if (toSplit.charAt(i) != delimiter) {

					buffer.append(toSplit.charAt(i));
				} else {
					if (buffer.toString().trim().length() == 0 && ignoreEmpty) {

					} else {
						stringStack.addElement(buffer.toString());
					}
					buffer = new StringBuffer();
				}
			}
		} catch (StringIndexOutOfBoundsException e) {
			System.out.println("[StringUtil.split] " + e.toString());
		}
		if (buffer.length() != 0)
			stringStack.addElement(buffer.toString());

		String[] split = new String[stringStack.size()];
		for (int i = 0; i < split.length; i++) {
			split[split.length - 1 - i] = (String) stringStack.pop();
		}
		stringStack = null;
		buffer = null;
		return split;
	}

	/**
	 * Splits the provided text into an array with a maximum length, separators
	 * specified.
	 * 
	 * @param str
	 *            the String to parse, may be null.
	 * @param separatorChars
	 *            the characters used as the delimiters, null splits on
	 *            whitespace
	 * @param max
	 *            the maximum number of elements to include in the array. A zero
	 *            or negative value implies no limit
	 * 
	 * @return an array of parsed Strings, null if null String input.
	 */
	public static String[] split(final String str, final String separatorChars, final int max) {
		return splitWorker(str, separatorChars, max, false);
	}

	private static String[] splitWorker(final String str, final String separatorChars, final int max,
			final boolean preserveAllTokens) {

		String[] result = null;

		if (str != null && str.length() == 0) {
			result = new String[0];
		} else if (str != null) {
			int len = str.length();
			Vector list = new Vector();
			int sizePlus1 = 1;
			int i = 0, start = 0;
			boolean match = false;
			boolean lastMatch = false;
			if (separatorChars == null) {
				// Null separator means use whitespace
				while (i < len) {
					if (SPACE == str.charAt(i)) {
						if (match || preserveAllTokens) {
							lastMatch = true;
							if (sizePlus1++ == max) {
								i = len;
								lastMatch = false;
							}
							list.addElement(str.substring(start, i));
							match = false;
						}
						start = ++i;
						continue;
					}
					lastMatch = false;
					match = true;
					i++;
				}
			} else if (separatorChars.length() == 1) {
				// Optimise 1 character case
				char sep = separatorChars.charAt(0);
				while (i < len) {
					if (str.charAt(i) == sep) {
						if (match || preserveAllTokens) {
							lastMatch = true;
							if (sizePlus1++ == max) {
								i = len;
								lastMatch = false;
							}
							list.addElement(str.substring(start, i));
							match = false;
						}
						start = ++i;
						continue;
					}
					lastMatch = false;
					match = true;
					i++;
				}
			} else {
				// standard case
				while (i < len) {
					if (separatorChars.indexOf(str.charAt(i)) >= 0) {
						if (match || preserveAllTokens) {
							lastMatch = true;
							if (sizePlus1++ == max) {
								i = len;
								lastMatch = false;
							}
							list.addElement(str.substring(start, i));
							match = false;
						}
						start = ++i;
						continue;
					}
					lastMatch = false;
					match = true;
					i++;
				}
			}
			if (match || (preserveAllTokens && lastMatch)) {
				list.addElement(str.substring(start, i));
			}

			result = new String[list.size()];
			for (int j = 0; j < list.size(); j++) {
				result[j] = (String) list.elementAt(j);
			}
		}
		return result;

	}

	/**
	 * 
	 * @param base
	 *            String to compare
	 * @param start
	 *            Starting string
	 * @return <code>start</code> string
	 */
	public static boolean startsWithIgnoreCase(String base, String start) {
		if (base.length() < start.length()) {
			return false;
		}
		return base.regionMatches(true, 0, start, 0, start.length());
	}

	/**
	 * Strips any of a set of characters from the start and end of a String.
	 * This method allows control of the characters to be stripped.
	 * 
	 * A null input String returns null. An empty string ("") input returns the
	 * empty string.
	 * 
	 * If the stripChars String is null, whitespace is stripped
	 * 
	 * @param str
	 *            String to strip.
	 * @param stripChars
	 *            Characters to strip.
	 * @return The striped String, or an empty String if null input.
	 */
	public static String strip(final String str, final String stripChars) {
		String result = str;
		if (!isEmpty(str)) {
			result = stripEnd(stripStart(str, stripChars), stripChars);
		}
		return result;
	}

	/**
	 * Strips any defined character from the start and end of a String.
	 * 
	 * @param str
	 *            String to strip.
	 * @param character
	 *            Character to strip from string.
	 * @return Stripped string.
	 */
	private static String stripChar(final String str, final char character) {
		StringBuffer stripBuffer = new StringBuffer(str);
		boolean search = true;

		while (search && stripBuffer.length() > 0) {
			// strip the tail
			char stripChar = stripBuffer.charAt(stripBuffer.length() - 1);
			if (stripChar == character) {
				stripBuffer.deleteCharAt(stripBuffer.length() - 1);
			} else {
				search = false;
			}
			// strip the head
			if (stripBuffer.length() > 0) {
				stripChar = stripBuffer.charAt(0);
				if (stripChar == character) {
					stripBuffer.deleteCharAt(0);
					search = true;
				}
			}
		}
		return stripBuffer.toString();
	}

	/**
	 * Strips any of a set of characters from the end of a String. A null input
	 * String returns null. An empty string ("") input returns the empty string.
	 * 
	 * @param str
	 *            The String to remove characters from, may be null.
	 * @param stripChars
	 *            The characters to remove, null treated as whitespace.
	 * @return The stripped String, null if null String input.
	 */
	public static String stripEnd(final String str, final String stripChars) {
		String result = str;
		if (!isEmpty(str)) {

			int end = str.length();
			if (stripChars == null) {
				while ((end != 0) && SPACE == str.charAt(end - 1)) {
					end--;
				}
			} else if (stripChars.length() != 0) {
				while ((end != 0) && (stripChars.indexOf(str.charAt(end - 1)) != -1)) {
					end--;
				}
			}
			result = str.substring(0, end);
		}
		return result;
	}

	/**
	 * Strips any of a set of characters from the start of a String. A null
	 * input String returns null. An empty string ("") input returns the empty
	 * string.
	 * 
	 * @param str
	 *            the String to remove characters from, may be null.
	 * @param stripChars
	 *            the characters to remove, null treated as whitespace.
	 * @return The stripped String or null(if str is null.
	 */
	public static String stripStart(final String str, final String stripChars) {
		String result = str;
		if (!isEmpty(str)) {
			int start = 0;
			if (stripChars == null) {
				while ((start != str.length()) && SPACE == str.charAt(start)) {
					start++;
				}
			} else if (stripChars.length() != 0) {
				while ((start != str.length()) && (stripChars.indexOf(str.charAt(start)) != -1)) {
					start++;
				}
			}
			result = str.substring(start);
		}
		return result;
	}

	/**
	 * Check if a character occurs consecutively in the given string
	 * 
	 * @param string
	 * @param chr
	 *            Charcter to check for consecutive occurence
	 * @return true if <code>chr</code> occurs consecutively
	 */
	public static boolean isConsecutive(String string, char chr) {
		return isConsecutive(string.toCharArray(), chr);
	}

	/**
	 * 
	 * @param array
	 * @param chr
	 * @return
	 */
	public static boolean isConsecutive(char[] array, char chr) {
		boolean firstPosSet = false;
		int firstPos = 0;
		int secPos = 0;
		for (int i = 0; i < array.length; i++) {
			if (array[i] == chr) {
				if (!firstPosSet) {
					firstPos = i;
					firstPosSet = true;
				} else {
					if (secPos == 0)
						secPos = i;
				}

			}
		}
		return secPos == firstPos + 1;
	}

	/**
	 * Wraps the given string so it fits on the specified lines. First of all it
	 * is split at the line-breaks ('\n'), subsequently the substrings are split
	 * when they do not fit on a single line.
	 * 
	 * @param string
	 *            the string which should be wrapped
	 * @param font
	 *            the font which is used to display the font
	 * @param firstLineWidth
	 *            the allowed width for the first line
	 * @param lineWidth
	 *            the allowed width for all other lines, lineWidth >=
	 *            firstLineWidth
	 * @return the array containing the substrings
	 */
	public static final String[] doTextWrap(final String string, final BitmapFont font, int firstLineWidth,
			final int lineWidth) {
		// if (firstLineWidth <= 0 || lineWidth <= 0) {
		// System.out.println("INVALID LINE WIDTH FOR SPLITTING " +
		// firstLineWidth + " / " + lineWidth
		// + " ( for string " + value + ")");
		// return new String[] { value };
		// }
		boolean hasLineBreaks = (string.indexOf('\n') != -1);
		int completeWidth = font.stringWidth(string);
		if ((completeWidth <= firstLineWidth && !hasLineBreaks)) { // || (value.
			// length()
			// <= 1) ) {
			// the given string fits on the first line:
			// if (hasLineBreaks) {
			// return split( "complete/linebreaks:" + completeWidth + "> " +
			// value, '\n');
			// } else {
			return new String[] { string };
			// }
		}
		// the given string does not fit on the first line:
		Vector lines = new Vector();
		if (!hasLineBreaks) {
			doTextWrap(string, font, completeWidth, firstLineWidth, lineWidth, lines);
		} else {
			// now the string will be splitted at the line-breaks and
			// then each line is processed:
			char[] valueChars = string.toCharArray();
			int lastIndex = 0;
			char c = ' ';
			int lineBreakCount = 0;
			for (int i = 0; i < valueChars.length; i++) {
				c = valueChars[i];
				boolean isCRLF = (c == 0x0D && i < valueChars.length - 1 && valueChars[i + 1] == 0x0A);
				if (c == '\n' || i == valueChars.length - 1 || isCRLF) {
					lineBreakCount++;
					String line = null;
					if (i == valueChars.length - 1) {
						line = new String(valueChars, lastIndex, (i + 1) - lastIndex);
						// System.out.println("wrap: adding last line " + line
						// );
					} else {
						line = new String(valueChars, lastIndex, i - lastIndex);
						// System.out.println("wrap: adding " + line );
					}
					completeWidth = font.stringWidth(line);
					if (completeWidth <= firstLineWidth) {
						lines.addElement(line);
					} else {
						doTextWrap(line, font, completeWidth, firstLineWidth, lineWidth, lines);
					}
					if (isCRLF) {
						i++;
					}
					lastIndex = i + 1;
					// after the first line all line widths are the same:
					firstLineWidth = lineWidth;
				} // for each line
			} // for all chars
			// special case for lines that end with \n: add a further line
			if (lineBreakCount > 1 && (c == '\n' || c == 10)) {
				lines.addElement(" ");
			}
		}
		// System.out.println("Wrapped [" + value + "] into " + lines.size() +
		// " rows.");
		final String[] ret = new String[lines.size()];
		lines.copyInto(ret);
		// System.out.println("Array Size " + ret.length);
		return ret;// (String[]) CollectionUtil.toArray(lines);//
		// ines.toArray(new
		// String
		// [lines.size()]);
	}

	/**
	 * Wraps the given string so that the substrings fit into the the given
	 * line-widths. It is expected that the specified lineWidth >=
	 * firstLineWidth. The resulting substrings will be added to the given
	 * ArrayList. When the complete string fits into the first line, it will be
	 * added to the list. When the string needs to be split to fit on the lines,
	 * it is tried to split the string at a gap between words. When this is not
	 * possible, the given string will be split in the middle of the
	 * corresponding word.
	 * 
	 * 
	 * @param value
	 *            the string which should be wrapped
	 * @param font
	 *            the font which is used to display the font
	 * @param completeWidth
	 *            the complete width of the given string for the specified font.
	 * @param firstLineWidth
	 *            the allowed width for the first line
	 * @param lineWidth
	 *            the allowed width for all other lines, lineWidth >=
	 *            firstLineWidth
	 * @param list
	 *            the list to which the substrings will be added.
	 */
	private final static void doTextWrap(String value, BitmapFont font, int completeWidth, int firstLineWidth,
			int lineWidth, Vector list) {
		char[] valueChars = value.toCharArray();
		int startPos = 0;
		int lastSpacePos = -1;
		int lastSpacePosLength = 0;
		int currentLineWidth = 0;
		for (int i = 0; i < valueChars.length; i++) {
			char c = valueChars[i];
			currentLineWidth += font.charWidth(c);
			if (c == '\n') {
				list.addElement(new String(valueChars, startPos, i - startPos));
				lastSpacePos = -1;
				startPos = i + 1;
				currentLineWidth = 0;
				firstLineWidth = lineWidth;
				i = startPos;
			} else if (currentLineWidth > firstLineWidth && i > 0) {
				if (c == ' ' || c == '\t') {
					list.addElement(new String(valueChars, startPos, i - startPos));
					startPos = ++i;
					currentLineWidth = 0;
					lastSpacePos = -1;
				} else if (lastSpacePos == -1) {
					if (i > startPos + 1) {
						i--;
					}
					// System.out.println("value=" + value + ", i=" + i +
					// ", startPos=" + startPos);
					list.addElement(new String(valueChars, startPos, i - startPos));
					startPos = i;
					currentLineWidth = 0;
				} else {
					currentLineWidth -= lastSpacePosLength;
					list.addElement(new String(valueChars, startPos, lastSpacePos - startPos));
					startPos = lastSpacePos + 1;
					lastSpacePos = -1;
				}
				firstLineWidth = lineWidth;
			} else if (c == ' ' || c == '\t') {
				lastSpacePos = i;
				lastSpacePosLength = currentLineWidth;
			}

		}
		// add tail:
		list.addElement(new String(valueChars, startPos, valueChars.length - startPos));

	}

	/**
	 * Wraps the given string so it fits on the specified lines. First of all it
	 * is split at the line-breaks ('\n'), subsequently the substrings are split
	 * when they do not fit on a single line.
	 * 
	 * @param string
	 *            The string which should be wrapped
	 * @param font
	 *            The font which is used to display the font
	 * @param firstLineWidth
	 *            The allowed width for the first line
	 * @param lineWidth
	 *            The allowed width for all other lines, lineWidth >=
	 *            firstLineWidth
	 * @return The array containing the substrings
	 */
	public static final String[] doTextWrap(final String string, final javax.microedition.lcdui.Font font,
			int firstLineWidth, final int lineWidth) {
		boolean hasLineBreaks = (string.indexOf('\n') != -1);
		int completeWidth = font.stringWidth(string);
		if ((completeWidth <= firstLineWidth && !hasLineBreaks)) { // || (value.
			// length()
			// <= 1) ) {
			// the given string fits on the first line:
			// if (hasLineBreaks) {
			// return split( "complete/linebreaks:" + completeWidth + "> " +
			// value, '\n');
			// } else {
			return new String[] { string };
			// }
		}
		// the given string does not fit on the first line:
		Vector lines = new Vector();
		if (!hasLineBreaks) {
			wrap(string, font, completeWidth, firstLineWidth, lineWidth, lines);
		} else {
			// now the string will be splitted at the line-breaks and
			// then each line is processed:
			char[] valueChars = string.toCharArray();
			int lastIndex = 0;
			char c = ' ';
			int lineBreakCount = 0;
			for (int i = 0; i < valueChars.length; i++) {
				c = valueChars[i];
				boolean isCRLF = (c == 0x0D && i < valueChars.length - 1 && valueChars[i + 1] == 0x0A);
				if (c == '\n' || i == valueChars.length - 1 || isCRLF) {
					lineBreakCount++;
					String line = null;
					if (i == valueChars.length - 1) {
						line = new String(valueChars, lastIndex, (i + 1) - lastIndex);
						// System.out.println("wrap: adding last line " + line
						// );
					} else {
						line = new String(valueChars, lastIndex, i - lastIndex);
						// System.out.println("wrap: adding " + line );
					}
					completeWidth = font.stringWidth(line);
					if (completeWidth <= firstLineWidth) {
						lines.addElement(line);
					} else {
						wrap(line, font, completeWidth, firstLineWidth, lineWidth, lines);
					}
					if (isCRLF) {
						i++;
					}
					lastIndex = i + 1;
					// after the first line all line widths are the same:
					firstLineWidth = lineWidth;
				} // for each line
			} // for all chars
			// special case for lines that end with \n: add a further line
			if (lineBreakCount > 1 && (c == '\n' || c == 10)) {
				lines.addElement(" ");
			}
		}
		// System.out.println("Wrapped [" + value + "] into " + lines.size() +
		// " rows.");
		String[] ret = new String[lines.size()];
		lines.copyInto(ret);
		// System.out.println("Array Size " + ret.length);
		return ret;// (String[]) CollectionUtil.toArray(lines);//
		// ines.toArray(new
		// String
		// [lines.size()]);
	}

	/**
	 * Wraps the given string so that the substrings fit into the the given
	 * line-widths. It is expected that the specified lineWidth >=
	 * firstLineWidth. The resulting substrings will be added to the given
	 * ArrayList. When the complete string fits into the first line, it will be
	 * added to the list. When the string needs to be split to fit on the lines,
	 * it is tried to split the string at a gap between words. When this is not
	 * possible, the given string will be split in the middle of the
	 * corresponding word.
	 * 
	 * 
	 * @param value
	 *            The string which should be wrapped
	 * @param font
	 *            The font which is used to display the font
	 * @param completeWidth
	 *            The complete width of the given string for the specified font.
	 * @param firstLineWidth
	 *            The allowed width for the first line
	 * @param lineWidth
	 *            The allowed width for all other lines, lineWidth >=
	 *            firstLineWidth
	 * @param list
	 *            The list to which the substrings will be added.
	 */
	private static void wrap(String value, javax.microedition.lcdui.Font font, int completeWidth, int firstLineWidth,
			int lineWidth, Vector list) {
		char[] valueChars = value.toCharArray();
		int startPos = 0;
		int lastSpacePos = -1;
		int lastSpacePosLength = 0;
		int currentLineWidth = 0;
		for (int i = 0; i < valueChars.length; i++) {
			char c = valueChars[i];
			currentLineWidth += font.charWidth(c);
			if (c == '\n') {
				list.addElement(new String(valueChars, startPos, i - startPos));
				lastSpacePos = -1;
				startPos = i + 1;
				currentLineWidth = 0;
				firstLineWidth = lineWidth;
				i = startPos;
			} else if (currentLineWidth > firstLineWidth && i > 0) {
				if (c == ' ' || c == '\t') {
					list.addElement(new String(valueChars, startPos, i - startPos));
					startPos = ++i;
					currentLineWidth = 0;
					lastSpacePos = -1;
				} else if (lastSpacePos == -1) {
					if (i > startPos + 1) {
						i--;
					}
					// System.out.println("value=" + value + ", i=" + i +
					// ", startPos=" + startPos);
					list.addElement(new String(valueChars, startPos, i - startPos));
					startPos = i;
					currentLineWidth = 0;
				} else {
					currentLineWidth -= lastSpacePosLength;
					list.addElement(new String(valueChars, startPos, lastSpacePos - startPos));
					startPos = lastSpacePos + 1;
					lastSpacePos = -1;
				}
				firstLineWidth = lineWidth;
			} else if (c == ' ' || c == '\t') {
				lastSpacePos = i;
				lastSpacePosLength = currentLineWidth;
			}

		}
		// add tail:
		list.addElement(new String(valueChars, startPos, valueChars.length - startPos));

	}

	/**
	 * Returns a string that fits into the given<code> width</code>
	 * 
	 * @param string
	 *            to fit
	 * @param width
	 *            of the area to render the string
	 * @param font
	 *            used for rendering
	 * @return <code>string</code> if it fits in the string width, else the
	 *         string content that fits and with trailing dots (...)
	 */
	public static String getFittingString(String string, int width, javax.microedition.lcdui.Font font) {
		if (font.stringWidth(string) < width)
			return string;
		else {
			int endPad = font.stringWidth("...");
			for (int i = 0; i < string.length(); i++) {
				if (font.substringWidth(string, 0, i) + endPad > width) {
					endPad = i;
					break;
				}
			}
			/* final check to see if it conforms to the width */
			if (font.stringWidth(string.substring(0, endPad) + "...") > width)
				for (; font.stringWidth(string.substring(0, endPad) + "...") > width;) {
					endPad--;
				}
			return new String(string.substring(0, endPad) + "...");
		}
	}

	private StringUtil() {}
}